package net.zoneland.knowledge.controller;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import net.zoneland.knowledge.constant.ApproveConstant;
import net.zoneland.knowledge.constant.BizConstant;
import net.zoneland.knowledge.constant.Constants;
import net.zoneland.knowledge.enums.AttachTypeEnum;
import net.zoneland.knowledge.enums.ChapterApproveFormExcelEnum;
import net.zoneland.knowledge.enums.ChapterDocTypeEnum;
import net.zoneland.knowledge.enums.WorkStatusEnum;
import net.zoneland.knowledge.exceptions.WorkApplyException;
import net.zoneland.knowledge.model.LoginUserPojo;
import net.zoneland.knowledge.model.NextNodeInfo;
import net.zoneland.knowledge.model.OptionVo;
import net.zoneland.knowledge.model.ResponseResult;
import net.zoneland.knowledge.model.WorkApplyInfo;
import net.zoneland.knowledge.model.WorkApplyInfoAttachment;
import net.zoneland.knowledge.service.ChapterApplyInfoService;
import net.zoneland.knowledge.service.PartyService;
import net.zoneland.knowledge.service.RoleService;
import net.zoneland.uniflow.client.UserInfo;

/**
 * 用印申请信息处理控制器.
 * @author bcc
 */
@RestController
@RequestMapping("/api/chapter")
public class ChapterApplyInfoController extends AbstractBaseApplyInfoController {
    /**
     * 日志.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ChapterApplyInfoController.class);

    /**
     * 附件的参数名.
     */
    private static final String FILE_NAME = "file";

    /**
     * 直属党委负责人uid
     */
    private static final String PARTY_LEADER_UID = "partyLeaderUid";

    /**
     * 直属党委负责人姓名
     */
    private static final String PARTY_LEADER_NAME = "partyLeaderName";

    /**
     * 直属党委用章
     */
    private static final String PARTY_COMMITTEE = "partyCommittee";

    /**
     * 下载用章申请审批表的名称(用章申请审批表.xlsx).
     * {@code URLEncoder.encode("用章申请审批表.xlsx", "UTF-8")}后的值
     * {@value #DOWNLOAD_CHAPTER_APPROVE_TABLE_NAME}
     */
    private static final String DOWNLOAD_CHAPTER_APPROVE_TABLE_NAME =
            "%E7%94%A8%E7%AB%A0%E7%94%B3%E8%AF%B7%E5%AE%A1%E6%89%B9%E8%A1%A8.xlsx";
    public static final String WORK_APPLY_ID_CAN_NOT_EMPTY = "工单信息id不能为空！";
    /**
     * 工单处理业务接口.
     */
    @Autowired
    private ChapterApplyInfoService chapterApplyInfoService;


    @Autowired
    private RoleService roleService;

    @Autowired
    private PartyService partyService;


    /**
     * 获取用印类型.
     * @return
     */
    @GetMapping("/getChapterDocTypes")
    public ResponseResult getChapterDocTypes() {

        final ResponseResult responseResult = new ResponseResult();
        responseResult.setData(Arrays.stream(ChapterDocTypeEnum.values()).map(item ->
                new OptionVo(item.getValue(), item.getName())).collect(Collectors.toList()));
        return responseResult;
    }

    /**
     * 获取直属党委负责人.
     * @return
     */
    @GetMapping("/getZsdwfzr")
    public ResponseResult getZsdwfzr() {

        final ResponseResult responseResult = new ResponseResult();
        final List<UserInfo> users = roleService.getUserInfoFromRole(Constants.ROLE_ZSDWFZR);
        responseResult.setData(users);
        return responseResult;
    }

    /**
     * 保存草稿.
     * @param request 请求
     * @return 响应
     */
    @PostMapping("/saveDraft")
    public ResponseResult saveDraft(final HttpServletRequest request) {

        final ResponseResult res = new ResponseResult();
        try {
            final LoginUserPojo user = getAndCheckCurrentUser(request);
            final String workApplyInfo = request.getParameter(WORK_APPLY_INFO_JSON_NAME);
            final WorkApplyInfo info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            this.initChapterApplyInfo(request, info, user);
            chapterApplyInfoService.saveDraft(info, user);
            res.setData(info.getId());
        } catch (final Exception e) {
            LOGGER.error("保存工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("保存工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 提交草稿工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/submitDraft")
    public ResponseResult submitDraft(final HttpServletRequest request) {

        final ResponseResult res = new ResponseResult();
        try {
            final LoginUserPojo user = getAndCheckCurrentUser(request);
            // 提交草稿工单
            final String workApplyInfo = request.getParameter(WORK_APPLY_INFO_JSON_NAME);
            final WorkApplyInfo info = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            this.initChapterApplyInfo(request, info, user);
            final String taskId = request.getParameter(TASK_ID_NAME);
            final String opinion = request.getParameter(OPINION_NAME);
            chapterApplyInfoService.submitDraft(info, taskId, opinion, user);
        } catch (final Exception e) {
            LOGGER.error("提交草稿工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("提交草稿工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 提交工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/submitChapterTask")
    public ResponseResult submitChapterTask(final HttpServletRequest request) {

        final ResponseResult res = new ResponseResult();
        try {
            final LoginUserPojo user = getAndCheckCurrentUser(request);
            // 提交工单
            final String opinion = request.getParameter(OPINION_NAME);
            final String workApplyId = request.getParameter(WORK_APPLY_ID_NAME);
            Preconditions.checkArgument(StringUtils.isNotBlank(workApplyId), WORK_APPLY_ID_CAN_NOT_EMPTY);
            final String taskId = request.getParameter(TASK_ID_NAME);
            Preconditions.checkArgument(StringUtils.isNotBlank(taskId), TASK_ID_BLANK_ERROR_MSG);
            final WorkApplyInfo info = workApplyInfoService.findWorkApplyInfoById(workApplyId);
            setDwfzr(request, info);
            Preconditions.checkNotNull(info, APPLY_NOT_EXIST + workApplyId);
            chapterApplyInfoService.submit(taskId, info, opinion, user);
        } catch (final Exception e) {
            LOGGER.error("提交工单信息异常", e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("提交工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 转交工单信息.
     * @param request http请求信息
     * @param opinion 审批意见
     * @param workApplyId 工单id
     * @param taskId 任务id
     * @param transferUserId 转交人id
     * @param transferUserName 转交人中文名
     * @return
     */
    @PostMapping("/transferTask")
    public ResponseResult transferTask(final HttpServletRequest request, @RequestParam(OPINION_NAME) final String opinion,
                                       @RequestParam(WORK_APPLY_ID_NAME) final String workApplyId, @RequestParam(TASK_ID_NAME) final String taskId,
                                       @RequestParam(Constants.TRANSFER_USER_ID) final String transferUserId, @RequestParam(Constants.TRANSFER_USER_NAME) final String transferUserName) {
        try {
            // 验证并获取当前用户
            final LoginUserPojo user = getAndCheckCurrentUser(request);
            // 校验数据完整性
            Preconditions.checkArgument(StringUtils.isNotBlank(workApplyId), WORK_APPLY_ID_CAN_NOT_EMPTY);
            Preconditions.checkArgument(StringUtils.isNotBlank(taskId), TASK_ID_BLANK_ERROR_MSG);
            // 获取工单信息
            final WorkApplyInfo info = workApplyInfoService.findWorkApplyInfoById(workApplyId);
            Preconditions.checkNotNull(info, APPLY_NOT_EXIST + workApplyId);
            // 转交工单
            chapterApplyInfoService.transfer(taskId, info, StringUtils.defaultIfBlank(opinion, ApproveConstant.DEFAULT_TRANSFER_NAME), user, new UserInfo(transferUserId, transferUserName));
        } catch (final IllegalArgumentException | NullPointerException | WorkApplyException e) {
            final ResponseResult result = new ResponseResult();
            result.setResult(ResponseResult.FAILED_RESULT);
            result.setMessage("转交工单信息失败！");
            LOGGER.error(result.getMessage() + "，工单id：{}，转交人id{}", workApplyId, transferUserId, e);
            return result;
        }
        return new ResponseResult();
    }

    /**
     * 设置党委负责人.
     * @param request 请求
     * @param info 工单
     */
    private static void setDwfzr(final HttpServletRequest request, final WorkApplyInfo info) {
        // 任务状态是直属党委经办人，或者是拟稿节点并且用章类型是直属党委用章，需要设置直属党委负责人
        if (WorkStatusEnum.ZSDWJBR == info.getStatus()
                || (WorkStatusEnum.DRAFT == info.getStatus() && StringUtils.equals(info.getUseSealType(), PARTY_COMMITTEE))) {
            final String partyLeaderUid = request.getParameter(PARTY_LEADER_UID);
            final String partyLeaderName = request.getParameter(PARTY_LEADER_NAME);
            info.setPartyLeaderUid(partyLeaderUid);
            info.setPartyLeaderName(partyLeaderName);
        }
    }

    /**
     * 印章申请退回.
     * @param request http请求
     * @param backToDraft 是否退回起草
     * @return 处理结果
     */
    @PostMapping("/chapterReject")
    public ResponseResult chapterReject(final HttpServletRequest request, final boolean backToDraft) {

        final ResponseResult res = new ResponseResult();
        // id
        final String workApplyId = request.getParameter(WORK_APPLY_ID_NAME);
        try {
            final LoginUserPojo user = getAndCheckCurrentUser(request);
            Preconditions.checkArgument(StringUtils.isNotBlank(workApplyId), WORK_APPLY_ID_CAN_NOT_EMPTY);
            final String taskId = request.getParameter(TASK_ID_NAME);
            Preconditions.checkArgument(StringUtils.isNotBlank(taskId), TASK_ID_BLANK_ERROR_MSG);
            final String opinion = request.getParameter(OPINION_NAME);
            Preconditions.checkArgument(StringUtils.isNotBlank(opinion), "退回原因不能为空！");
            final WorkApplyInfo info = workApplyInfoService.findWorkApplyInfoById(workApplyId);
            Preconditions.checkNotNull(info, APPLY_NOT_EXIST + workApplyId);
            if (backToDraft) {
                chapterApplyInfoService.backToDraft(workApplyId, taskId, opinion, user);
            } else {
                chapterApplyInfoService.chapterReject(workApplyId, taskId, opinion, user);
            }
        } catch (final Exception e) {
            LOGGER.error("提交工单信息异常,workApplyId:{}", workApplyId, e);
            res.setResult(ResponseResult.FAILED_RESULT);
            res.setMessage("提交工单信息异常：" + e.getMessage());
        }
        return res;
    }

    /**
     * 印章申请退回起草.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/backToDraft")
    public ResponseResult backToDraft(final HttpServletRequest request) {
        return chapterReject(request, true);
    }


    /**
     * 提交工单信息.
     * @param request http请求
     * @return 处理结果
     */
    @PostMapping("/downloadApproveForm")
    public void downloadApproveForm(final HttpServletRequest request, final HttpServletResponse response)
            throws IOException, InvalidFormatException {
        // 工单id
        final String workApplyId = request.getParameter(WORK_APPLY_ID_NAME);
        Preconditions.checkArgument(StringUtils.isNotBlank(workApplyId), WORK_APPLY_ID_CAN_NOT_EMPTY);
        final WorkApplyInfo info = workApplyInfoService.findWorkApplyInfoById(workApplyId);

        try (final InputStream classInputStream = ChapterApplyInfoController.class.getResourceAsStream(
                "/excelTemplate/chapterApproveFormTemplate.xlsx");
             final Workbook workbook = WorkbookFactory.create(classInputStream)) {

            final Sheet sheet = workbook.getSheetAt(0);

            //设置excel的内容
            Arrays.stream(ChapterApproveFormExcelEnum.values()).forEach(item -> item.setCellValue(sheet, info));

            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            // 非常重要
            response.reset();
            response.setContentType("application/x-msdownload");
            response.addHeader("Content-Disposition", "attachment; filename=" + DOWNLOAD_CHAPTER_APPROVE_TABLE_NAME);
            final ServletOutputStream outputStream = response.getOutputStream();
            workbook.write(outputStream);
            outputStream.flush();
            response.flushBuffer();
        }
    }

    /**
     * 获取下一节点信息
     * @param request 请求数据对象
     * @param decision 决策名称
     * @param workApplyId 工单id
     * @param workApplyInfo 工单数据
     * @return 请求返回对象
     */
    @PostMapping("/nextNodeInfo")
    public ResponseResult nextNodeInfo(final HttpServletRequest request,
                                       @RequestParam(DECISION_NAME) final String decision,
                                       @RequestParam(WORK_APPLY_ID_NAME) final String workApplyId,
                                       @RequestParam(WORK_APPLY_INFO_JSON_NAME) final String workApplyInfo) {
        try {
            final WorkApplyInfo infoForm = objectMapper.readValue(workApplyInfo, WorkApplyInfo.class);
            if (StringUtils.isEmpty(workApplyId)) {
                // 获取用章申请流程在拟稿节点的下一个任务状态
                final WorkStatusEnum nextSubmitStatus = WorkStatusEnum.getChapterDraftNextStatus(infoForm.getUseSealType());
                final NextNodeInfo nextNodeInfo = new NextNodeInfo();
                nextNodeInfo.setNodeId(nextSubmitStatus.getValue());
                nextNodeInfo.setNodeName(nextSubmitStatus.getChapterNodeName());
                nextNodeInfo.setUserInfo(nextSubmitStatus.getHandler(infoForm));
                return new ResponseResult(nextNodeInfo);
            }

            final WorkApplyInfo info = workApplyInfoService.findWorkApplyInfoById(workApplyId);
            // 因为前端有可能更新用章类型，所以需要用form数据覆盖数据库中的原数据
            if(StringUtils.isNotBlank(infoForm.getUseSealType())) {
                info.setUseSealType(infoForm.getUseSealType());
            }
            setDwfzr(request, info);
            final WorkStatusEnum nextSubmitStatus = getNextWorkStatus(decision, info, infoForm);
            final NextNodeInfo nextNodeInfo = new NextNodeInfo();
            nextNodeInfo.setUserInfo(nextSubmitStatus.getHandler(info));
            nextNodeInfo.setNodeId(nextSubmitStatus.getValue());
            nextNodeInfo.setNodeName(nextSubmitStatus.getChapterNodeName());
            return new ResponseResult(nextNodeInfo);
        } catch (final IllegalArgumentException | NullPointerException | JsonProcessingException e) {
            final ResponseResult result = new ResponseResult();
            result.setResult(ResponseResult.FAILED_RESULT);
            result.setMessage("获取下一节点信息失败！");
            LOGGER.error(result.getMessage() + "，决策名称：{}，工单id{}，工单数据{}", decision, workApplyId, workApplyInfo, e);
            return result;
        }
    }

    /**
     * 获取下一个任务状态
     * @param decision 决策名称
     * @param info 工单数据库数据
     * @param infoForm 工单form表单数据
     * @return 任务状态
     */
    private WorkStatusEnum getNextWorkStatus(final String decision, final WorkApplyInfo info, final WorkApplyInfo infoForm) {
        // 如果是退回拟稿决策，直接返回任务状态为拟稿
        if (StringUtils.equals(ApproveConstant.DECISION_BACK_TO_DRAFT, decision)) {
            return WorkStatusEnum.DRAFT;
        } else if (StringUtils.equals(ApproveConstant.DECISION_SUBMIT, decision)) {
            // 决策时提交，获取提交至下一步的任务状态
            return info.getStatus().getNextSubmitStatus(infoForm.getUseSealType());
        }
        // 否则获取退回上一步的任务状态
        return info.getStatus().getRejectStatus(infoForm.getUseSealType());
    }

    private void initChapterApplyInfo(final HttpServletRequest request, final WorkApplyInfo info, final LoginUserPojo user) {

        try {
            info.setFlowType(BizConstant.CHAPTER_APPLY_PROCESS_CODE);
            //数据库字段有非空验证
            info.setMainTaskName(info.getTitle());
            //参考文档附件
            final MultipartHttpServletRequest multipartReq = (MultipartHttpServletRequest) request;
            final List<WorkApplyInfoAttachment> newUploadAttachments = getAttachments(multipartReq, AttachTypeEnum.CHAPTER_FILE,
                                                                                      FILE_NAME, user);
            if (CollectionUtils.isNotEmpty(newUploadAttachments)) {
                final List<WorkApplyInfoAttachment> attachments =
                        ObjectUtils.defaultIfNull(info.getAttachments(), Lists.newArrayList());
                attachments.addAll(newUploadAttachments);
                info.setAttachments(attachments);
            }
        } catch (final Exception e) {
            LOGGER.error("初始化工单出错, 工单标题：{}", info.getTitle());
            throw new WorkApplyException("工单数据格式异常，请检查相应的数据是否完整！", e);
        }
    }

}
